﻿using System;
using System.Collections.Generic;


namespace HAVNet.Core.Lambda
{
    public delegate T Func <T> ();
    public delegate U Func <T, U> (T t);
    public delegate V Func <T, U, V> (T t, U u);
    public delegate W Func <T, U, V, W> (T t, U u, V v);
    public delegate X Func <T, U, V, W, X> (T t, U u, V v, W w);
    public delegate Y Func <T, U, V, W, X, Y> (T t, U u, V v, W w, X x);
    public delegate Z Func <T, U, V, W, X, Y, Z> (T t, U u, V v, W w, X x, Y y);
    public delegate void VFunc ();
    public delegate void VFunc <T>(T t);
    public delegate void VFunc <T, U>(T t, U u);
    public delegate void VFunc <T, U, V>(T t, U u, V v);
    public delegate void VFunc <T, U, V, W>(T t, U u, V v, W w);
    public delegate void VFunc <T, U, V, W, X>(T t, U u, V v, W w, X x);
    public delegate void VFunc <T, U, V, W, X, Y>(T t, U u, V v, W w, X x, Y y);

    public class Funcs
    {
        public static void pass ()
        {
            ;
        }

        public static IEnumerable<U> map<T, U> (Func<T, U> function, IEnumerable<T> ts)
        {
            foreach (T t in ts)
                yield return function (t);
        }

        public static void apply<T> (VFunc<T> action, IEnumerable<T> ts)
        {
            foreach (T t in ts)
                action (t);
        }

        public static IEnumerable<T> filter<T> (Predicate<T> p, IEnumerable<T> ts)
        {
            foreach (T t in ts)
                if (p(t))
                    yield return t;
        }

        public static IEnumerable<T> take<T> (int n, IEnumerable<T> ts)
        {
            int aux = 0;

            foreach (T t in ts)
            {
                yield return t;

                if (++ aux == n)
                    yield break;
            }
        }

        public static IEnumerable<T> takeWhile<T> (Predicate<T> p, IEnumerable<T> ts)
        {
            foreach (T t in ts)
            {
                if (! p(t))
                    yield break;

                yield return t;                
            }
        }

        public static IEnumerable<int> range (int top)
        {
            for (int i = 0; i < top; i ++)
                yield return i;
        }

        public static IEnumerable<int> range (int bottom, int top)
        {
            for (int i = bottom; i < top; i ++)
                yield return i;
        }

        public static IEnumerable<int> range (int bottom, int top, int step)
        {
            if (top < bottom)
            {
                for (int i = bottom; i > top; i += step)
                    yield return i;
            }
            else
            {
                for (int i = bottom; i < top; i += step)
                    yield return i;
            }

            //the top one is ugliest but more efficient and readable. alejandro varela.
            //for (int i = bottom; (top < bottom) ? i > top: i < top  ; i += step)
            //    yield return i;
        }

        public static IEnumerable<int> positiveIntegers ()
        {
            for (int i = 0; ; i ++)
                yield return i;
        }

        public static IEnumerable<int> naturals ()
        {
            for (int i = 1; ; i++)
                yield return i;
        }

        public static bool and (params bool [] booles)
        {
            foreach (bool boolX in booles)
                if (! boolX)
                    return false;

            return true;
        }
    }
}
